package edison.jcr;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.AccessControlException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.jcr.AccessDeniedException;
import javax.jcr.Credentials;
import javax.jcr.InvalidItemStateException;
import javax.jcr.InvalidSerializedDataException;
import javax.jcr.Item;
import javax.jcr.ItemExistsException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.LoginException;
import javax.jcr.NamespaceException;
import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.ReferentialIntegrityException;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.ValueFactory;
import javax.jcr.Workspace;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.retention.RetentionManager;
import javax.jcr.security.AccessControlManager;
import javax.jcr.version.VersionException;

import org.apache.commons.lang.StringUtils;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;

import edison.jcr.NameFactory.Name;
import edison.persistence.ItemService;
import edison.persistence.Journal;
import edison.persistence.PersistenceException;
import edison.persistence.SecurityService;

class SessionImpl implements Session {

	private final Repository repository;
	private final String workspaceName;
	private final SimpleCredentials credentials;
	private final ItemService itemService;
	private Journal journal;
	private SecurityService securityService;
	private final Map<String, String> namespaces = new HashMap<String, String>();

	public SessionImpl(Repository repository, String workspaceName, SimpleCredentials credentials) {
		this.repository = repository;
		this.credentials = credentials;
		this.workspaceName = workspaceName;
		this.itemService = null;
		initialNamespaces();
	}

	private void initialNamespaces() {
		namespaces.put("jcr", "http://www.jcp.org/jcr/1.0");
		namespaces.put("nt", "http://www.jcp.org/jcr/nt/1.0");
		namespaces.put("mix", "http://www.jcp.org/jcr/mix/1.0");
		namespaces.put("xml", "http://www.w3.org/XML/1998/namespace");
	}

	public Repository getRepository() {
		NameFactory s = new NameFactory();
		Name a = s.create("");
		return repository;
	}

	public String getUserID() {
		return credentials.getUserID();
	}

	public String[] getAttributeNames() {
		return credentials.getAttributeNames();
	}

	public Object getAttribute(String name) {
		return credentials.getAttribute(name);
	}

	public Workspace getWorkspace() {
		return new WorkspaceImpl(workspaceName, this);
	}

	public Node getRootNode() throws RepositoryException {
		try {
			String rootPath = itemService.getRootPath();
			return new NodeImpl(this, rootPath);
		} catch (PersistenceException e) {
			throw new RepositoryException(e);
		}

	}

	public Session impersonate(Credentials credentials) throws LoginException, RepositoryException {
		// TODO Auto-generated method stub
		return null;
	}

	public Node getNodeByUUID(String uuid) throws ItemNotFoundException, RepositoryException {
		// TODO Auto-generated method stub
		return null;
	}

	public Node getNodeByIdentifier(String id) throws ItemNotFoundException, RepositoryException {
		try {
			ItemBean item = itemService.getNodeById(id);
			if (item != null) {
				return new NodeImpl(this, item.getPath());
			}

			throw new ItemNotFoundException();
		} catch (PersistenceException e) {
			throw new RepositoryException(e);
		}
	}

	public Item getItem(String absPath) throws PathNotFoundException, RepositoryException {
		try {
			ItemBean itemBean = itemService.getItemByPath(absPath);
			if (itemBean == null)
				throw new PathNotFoundException();

			if (itemBean.isNode())
				return new NodeImpl(this, absPath);
			else
				return new PropertyImpl(this, absPath);

		} catch (PersistenceException e) {
			throw new RepositoryException(e);
		}
	}

	public Node getNode(String absPath) throws PathNotFoundException, RepositoryException {
		try {
			if (itemService.nodeExists(absPath))
				throw new PathNotFoundException();

			return new NodeImpl(this, absPath);
		} catch (PersistenceException e) {
			throw new RepositoryException(e);
		}
	}

	public Property getProperty(String absPath) throws PathNotFoundException, RepositoryException {
		try {
			if (itemService.propertyExists(absPath))
				throw new PathNotFoundException();

			return new PropertyImpl(this, absPath);
		} catch (PersistenceException e) {
			throw new RepositoryException(e);
		}
	}

	public boolean itemExists(String absPath) throws RepositoryException {
		try {
			return itemService.itemExists(absPath);
		} catch (PersistenceException e) {
			throw new RepositoryException(e);
		}
	}

	public boolean nodeExists(String absPath) throws RepositoryException {
		try {
			return itemService.nodeExists(absPath);
		} catch (PersistenceException e) {
			throw new RepositoryException(e);
		}
	}

	public boolean propertyExists(String absPath) throws RepositoryException {
		try {
			return itemService.propertyExists(absPath);
		} catch (PersistenceException e) {
			throw new RepositoryException(e);
		}
	}

	public void move(String srcAbsPath, String destAbsPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, RepositoryException {
		try {
			if (itemService.itemExists(srcAbsPath))
				throw new PathNotFoundException();

			if (itemService.itemExists(destAbsPath))
				throw new ItemExistsException();

			itemService.move(srcAbsPath, destAbsPath);
		} catch (PersistenceException e) {
			throw new RepositoryException(e);
		}
	}

	public void removeItem(String absPath) throws VersionException, LockException, ConstraintViolationException, AccessDeniedException, RepositoryException {
		try {
			itemService.removeItem(absPath);
		} catch (PersistenceException e) {
			throw new RepositoryException(e);
		}

	}

	public void save() throws AccessDeniedException, ItemExistsException, ReferentialIntegrityException, ConstraintViolationException, InvalidItemStateException, VersionException, LockException, NoSuchNodeTypeException, RepositoryException {
		journal.applyChanges();

	}

	public void refresh(boolean keepChanges) throws RepositoryException {
		journal.clear();

	}

	public boolean hasPendingChanges() throws RepositoryException {
		return journal.hasPendingChanges();
	}

	public ValueFactory getValueFactory() throws UnsupportedRepositoryOperationException, RepositoryException {
		Value v = this.getRepository().getDescriptorValue(Repository.WRITE_SUPPORTED);
		boolean WRITE_SUPPORTED = (v == null) ? null : v.getBoolean();
		if (WRITE_SUPPORTED)
			return new ValueFactoryImpl();

		throw new UnsupportedRepositoryOperationException();
	}

	public boolean hasPermission(String absPath, String actions) throws RepositoryException {
		try {
			return securityService.hasPermission(getUserID(), absPath, actions);
		} catch (PersistenceException e) {
			throw new RepositoryException(e);
		}
	}

	public void checkPermission(String absPath, String actions) throws AccessControlException, RepositoryException {
		boolean hasPermission = hasPermission(absPath, actions);
		if (!hasPermission)
			throw new AccessControlException(actions);

	}

	public boolean hasCapability(String methodName, Object target, Object[] arguments) throws RepositoryException {
		// TODO Auto-generated method stub
		return false;
	}

	public ContentHandler getImportContentHandler(String parentAbsPath, int uuidBehavior) throws PathNotFoundException, ConstraintViolationException, VersionException, LockException, RepositoryException {
		// TODO Auto-generated method stub
		return null;
	}

	public void importXML(String parentAbsPath, InputStream in, int uuidBehavior) throws IOException, PathNotFoundException, ItemExistsException, ConstraintViolationException, VersionException, InvalidSerializedDataException, LockException, RepositoryException {
		// TODO Auto-generated method stub

	}

	public void exportSystemView(String absPath, ContentHandler contentHandler, boolean skipBinary, boolean noRecurse) throws PathNotFoundException, SAXException, RepositoryException {
		// TODO Auto-generated method stub

	}

	public void exportSystemView(String absPath, OutputStream out, boolean skipBinary, boolean noRecurse) throws IOException, PathNotFoundException, RepositoryException {
		// TODO Auto-generated method stub

	}

	public void exportDocumentView(String absPath, ContentHandler contentHandler, boolean skipBinary, boolean noRecurse) throws PathNotFoundException, SAXException, RepositoryException {
		// TODO Auto-generated method stub

	}

	public void exportDocumentView(String absPath, OutputStream out, boolean skipBinary, boolean noRecurse) throws IOException, PathNotFoundException, RepositoryException {
		// TODO Auto-generated method stub

	}

	public void setNamespacePrefix(String prefix, String uri) throws NamespaceException, RepositoryException {
		if (StringUtils.isBlank(prefix) || StringUtils.isBlank(uri))
			throw new NamespaceException();

		if (prefix.startsWith("xml"))
			throw new NamespaceException("prefix beginning with the characters 'xml'");

		namespaces.put(prefix, uri);

	}

	public String[] getNamespacePrefixes() throws RepositoryException {
		return namespaces.keySet().toArray(new String[] {});
	}

	public String getNamespaceURI(String prefix) throws NamespaceException, RepositoryException {
		if (!namespaces.containsKey(prefix))
			throw new NamespaceException("the specified prefix is unknown");

		return namespaces.get(prefix);
	}

	public String getNamespacePrefix(String uri) throws NamespaceException, RepositoryException {
		if (!namespaces.containsKey(uri))
			throw new NamespaceException("the specified uri is unknown");

		for (Entry<String, String> entry : namespaces.entrySet()) {
			if (entry.getValue().equals(uri))
				return entry.getKey();
		}

		throw new RepositoryException();
	}

	public void logout() {
		// TODO Auto-generated method stub

	}

	public boolean isLive() {
		// TODO Auto-generated method stub
		return false;
	}

	public void addLockToken(String lt) {
		// TODO Auto-generated method stub

	}

	public String[] getLockTokens() {
		// TODO Auto-generated method stub
		return null;
	}

	public void removeLockToken(String lt) {
		// TODO Auto-generated method stub

	}

	public AccessControlManager getAccessControlManager() throws UnsupportedRepositoryOperationException, RepositoryException {
		// TODO Auto-generated method stub
		return null;
	}

	public RetentionManager getRetentionManager() throws UnsupportedRepositoryOperationException, RepositoryException {
		// TODO Auto-generated method stub
		return null;
	}

}